package com.quark.framework.caller;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.quark.framework.utility.QuarkUtilArrayConverter;
import com.quark.framework.utility.QuarkUtilDateHelper;

public class QuarkCallerBase {
	private Map<String, List<Method>> callMap = null;
	
	public QuarkCallerBase()
	{
		callMap = new HashMap<String, List<Method>>();
		Method[] methods = this.getClass().getMethods();
		for(Method i : methods)
		{			
			String mappingName = i.getName().toUpperCase() + i.getParameterTypes().length;
			if(!callMap.containsKey(mappingName))
			{
				callMap.put(mappingName, new ArrayList<Method>());
			}
			i.setAccessible(true);
			callMap.get(mappingName).add(i);
		}
	}
	
	public boolean hasCall(String callName, int paramCount)
	{
		if(callMap.containsKey(callName.toUpperCase() + paramCount))
			return true;
		return false;
	}
	
	//function parameters use form void func(int xx1,int xx2, int xx3)to indecate parameter order
	public QuarkCallResult call(String methodName, Map<String, String> map)
	{		
		String name = methodName + map.size();
		List<Method> callList = callMap.get(name);
		if(callList.isEmpty())
			return failed(methodName, "no such method!");
		//check if a map parameter is required
		if(map.size() == 1 && callList.size() == 1)
		{
			Method m = callList.get(0);
			if(m.getParameterTypes().length == 1 && m.getParameterTypes()[0].equals(Map.class))
				//a map call
			{
				try
				{
					return succeed(m.invoke(this, map));
				}
				catch(Exception e)
				{
					return failed(methodName, e.getLocalizedMessage());
				}
			}
		}
		
		//a argument list call
		Iterator<String> keys = map.keySet().iterator();
		String[] params = new String[map.size()];
		while(keys.hasNext())
		{
			String key = keys.next();
			char chIdx = key.charAt(key.length() - 1);
			int index = Character.getNumericValue(chIdx);
			if(index > 9)
			{
				return failed(methodName, "too much arguments.");
			}
			params[index] = map.get(key);
		}
		Object[] converted = null;
		for(int i = 0; i < callList.size(); i++)
		{
			Method m = callList.get(i);
			converted = convertParams(m.getParameterTypes(), params);
			if(converted != null)
			{
				try {
					return succeed(m.invoke(this, converted));
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		return failed(methodName, "arguments unmatch!");
	}
	
	//call with parameter values, params are ordered after method arguments
/*	public Object call(Method m, String[] params)
	{		
		Object[] converted = null;
			
		converted = convertParams(m.getParameterTypes(), params);
		if(converted != null)
		{
			try {
				return Succeed(m.invoke(this, converted));
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return Failed(m.getName(), e.getLocalizedMessage());
			}				
		}
		return Failed(m.getName(), "arguments unmatch!");
	}*/
	private static QuarkCallResult succeed(Object retObj)
	{
		QuarkCallResult rst = new QuarkCallResult();
		rst.exception = null;
		rst.isSuccessful = true;
		rst.result = retObj;
		return rst;
	}
	private static QuarkCallResult failed(String methodName, String message)
	{
		QuarkCallResult rst = new QuarkCallResult();
		rst.exception = new Exception("Call \"" + methodName + "\" failed," + message);
		rst.isSuccessful = false;
		return rst;
	}
	private static Object[] convertParams(Class<?>[] paramTypes, String[] params)
	{
		Object[] converted = new Object[params.length];
		for(int i = 0; i < params.length; i++)
		{
			if(paramTypes[i].equals(String.class))
				//if require a string
			{
				converted[i] = params[i];
			}
			else if(paramTypes[i].equals(Integer.class))
			{
				try
				{
					converted[i] = Integer.valueOf(params[i]);
				}
				catch(Exception e)
				{
					return null;
				}
			}
			else if(paramTypes[i].equals(Date.class))
			{
				try
				{
					converted[i] = QuarkUtilDateHelper.parseDate(params[i]);
				}
				catch(Exception e)
				{
					e.printStackTrace();
					return null;
				}				
			}
			else if(paramTypes[i].equals(List.class))
			{
				String[] items = params[i].split(",");
				List<String> list = new ArrayList<String>();
				for(int j = 0; j < items.length; j ++)
				{
					list.add(items[j]);
				}
				converted[i] = list;
			}
			else if(paramTypes[i].isArray())
			{
				Class<?> componentClass = paramTypes[i].getComponentType();
				
				if(componentClass.equals(String.class))
				{
					String[] items = params[i].split(",");
					if(items.length == 0)
						return null;
					converted[i] = items;
				}
				else if(componentClass.equals(Integer.class))
				{
					converted[i] = QuarkUtilArrayConverter.makeIntegerArray(params[i]);
					if(converted[i] == null)
						return null;
				}
				else if(componentClass.equals(Date.class))
				{
					converted[i] = QuarkUtilArrayConverter.makeDateArray(params[i]);
					if(converted[i] == null)
						return null;
				}
				else
				{
					return null;
				}
			}
		}
		return converted;
	}
}
